package io;

import io.bd.GestorBD;
import io.red.GestorConexiones;
import io.red.Conexion;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;

/**
 *
 * @author Mariano Zapata
 */
public class Procesador extends Thread {
    
    private ListaSincronizada<Conexion> conexiones;
    private Cola<Mensaje> colaMensajes;
    private GestorBD gestorBD;
    private GestorConexiones gestorCO;
    private int idConexiones;
    private ProcesadorListener listener;
    private boolean vida;
    private ProcesadorLibreria pol;
    
    public Procesador(int puertoObjeto, int puertoArchivo) throws IOException, ClassNotFoundException, SQLException
    {
        super("ProcesadorObjeto");
        conexiones= new ListaSincronizada<Conexion>();
        colaMensajes= new Cola<Mensaje>();
        listener= null;
        idConexiones= Integer.MAX_VALUE;
        vida= true;
        gestorBD= new GestorBD();
        pol= new ProcesadorLibreria(gestorBD);
        gestorBD.conectar();
        try {
            gestorCO= new GestorConexiones(puertoObjeto, puertoArchivo, this);
        } catch (IOException ex) {gestorBD.desconectar();
                                  throw ex;}
    }
    
    @Override
    public void run()
    {
        try {
            gestorCO.start();
            while (vida)
            {
                Mensaje actual= colaMensajes.desencolar();
                Object respuesta= this.spliter(actual);
                if (respuesta == null)
                {
                    Mensaje [] ret= new Mensaje[2];
                    ret[0]= new Mensaje(actual.obtenerTipo(), false);
                    ret[0].asignarDuenio(actual.obtenerDuenio());
                    ret[1]= null;
                    this.enviar(ret);
                }
                else
                {
                    if (respuesta instanceof Mensaje [])
                    {
                        Mensaje [] ret= (Mensaje []) respuesta;
                        ret[0].asignarDuenio(actual.obtenerDuenio());
                        if (ret[1] != null)
                            ret[1].asignarDuenio(actual.obtenerDuenio());                        
                        this.enviar(ret);
                    }
                    if (respuesta instanceof InputStream)
                    {
                        Mensaje [] ret= new Mensaje[2];
                        ret[0]= new Mensaje(actual.obtenerTipo(), true);
                        ret[0].asignarDuenio(actual.obtenerDuenio());
                        ret[1]= null;
                        this.enviar(ret);
                        this.enviar((InputStream) respuesta, actual.obtenerDuenio());
                    }
                }
            }
        } catch (InterruptedException ex) {vida= false;}
          catch (SQLException ex) {this.notificarError(ex.getMessage());}
          catch (IOException ex) {this.notificarError(ex.getMessage());}
    }
    
    private Object spliter(Mensaje msj) throws SQLException, IOException
    {
        Object ret= null;
        switch (msj.obtenerTipo())
        {
            case 0:
                ret= pol.expandirCarpeta((Carpeta) msj.obtenerCargaUtil());
                break;
            case 1:
                ret= pol.abrirCliente((Cliente) msj.obtenerCargaUtil(), msj.obtenerDuenio());
                break;
            case 2:
                ret= pol.cerrarCliente((Cliente) msj.obtenerCargaUtil());
                break;
            case 3:
                Object [] cu3= (Object []) msj.obtenerCargaUtil();
                ret= pol.crearCliente((Cliente) cu3[0], (Carpeta) cu3[1], msj.obtenerDuenio());
                break;
            case 4:
                ret= pol.guardarCliente((Cliente) msj.obtenerCargaUtil());
                break;
            case 5:
                Object [] cu5= (Object []) msj.obtenerCargaUtil();
                ret= pol.eliminarCliente((Cliente) cu5[0], (Carpeta) cu5[1], msj.obtenerDuenio());
                break;
            case 6:
                Object [] cu6= (Object []) msj.obtenerCargaUtil();
                ret= pol.cortarCliente((Cliente) cu6[0], (Carpeta) cu6[1], (Carpeta) cu6[2], msj.obtenerDuenio());
                break;
            case 7:
                Object [] cu7= (Object []) msj.obtenerCargaUtil();
                ret= pol.copiarCliente((Cliente) cu7[0], (Carpeta) cu7[1], msj.obtenerDuenio());
                break;
            case 8:
                Object [] cu8= (Object []) msj.obtenerCargaUtil();
                ret= pol.crearCarpeta((Carpeta) cu8[0], (Carpeta) cu8[1]);
                break;
            case 9:
                Object [] cu9= (Object []) msj.obtenerCargaUtil();
                ret= pol.guardarCarpeta((Carpeta) cu9[0], (Carpeta) cu9[1]);
                break;
            case 10:
                Object [] cu10= (Object []) msj.obtenerCargaUtil();
                ret= pol.eliminarCarpeta((Carpeta) cu10[0], (Carpeta) cu10[1], msj.obtenerDuenio());
                break;
            case 11:
                Object [] cu11= (Object []) msj.obtenerCargaUtil();
                ret= pol.cortarCarpeta((Carpeta) cu11[0], (Carpeta) cu11[1], (Carpeta) cu11[2], msj.obtenerDuenio());
                break;
            case 12:
                Object [] cu12= (Object []) msj.obtenerCargaUtil();
                ret= pol.copiarCarpeta((Carpeta) cu12[0], (Carpeta) cu12[1], msj.obtenerDuenio());
                break;
            case 13:
                Object [] cu13= (Object []) msj.obtenerCargaUtil();
                ret= pol.crearArchivo((Archivo) cu13[0], (Carpeta) cu13[1], this.obtenerStreamConexion(msj.obtenerDuenio()));
                break;
            case 14:
                ret= pol.abrirArchivo((Archivo) msj.obtenerCargaUtil(), msj.obtenerDuenio());
                break;
            case 15:
                Object [] cu15= (Object []) msj.obtenerCargaUtil();
                ret= pol.cambiarNombreArchivo((Archivo) cu15[0], (Carpeta) cu15[1], msj.obtenerDuenio());
                break;
            case 16:
                Object [] cu16= (Object []) msj.obtenerCargaUtil();
                ret= pol.cerrarArchivo((Archivo) cu16[0], (Boolean) cu16[1], this.obtenerStreamConexion(msj.obtenerDuenio()));
                break;
            case 17:
                Object [] cu17= (Object []) msj.obtenerCargaUtil();
                ret= pol.eliminarArchivo((Archivo) cu17[0], (Carpeta) cu17[1], msj.obtenerDuenio());
                break;
            case 18:
                Object [] cu18= (Object []) msj.obtenerCargaUtil();
                ret= pol.cortarArchivo((Archivo) cu18[0], (Carpeta) cu18[1], (Carpeta) cu18[2], msj.obtenerDuenio());
                break;
            case 19:
                Object [] cu19= (Object []) msj.obtenerCargaUtil();
                ret= pol.copiarArchivo((Archivo) cu19[0], (Carpeta) cu19[1], msj.obtenerDuenio());
                break;
            case 20:
                ret= pol.obtenerItemsAgenda((String) msj.obtenerCargaUtil());
                break;
            case 21:
                ret= pol.crearItemAgenda((ItemAgenda) msj.obtenerCargaUtil());
                break;
            case 22:
                ret= pol.eliminarItemAgenda((ItemAgenda) msj.obtenerCargaUtil());
                break;
            case 23:
                ret= pol.modificarItemAgenda((ItemAgenda) msj.obtenerCargaUtil());
                break;
            case 24:
                ret= pol.buscarCarpeta((String) msj.obtenerCargaUtil());
                break;
            case 25:
                ret= pol.buscarArchivo((String) msj.obtenerCargaUtil());
                break;
            case 26:
                ret= pol.buscarClienteApellido((String) msj.obtenerCargaUtil());
                break;
            case 27:
                ret= pol.buscarClienteNombre((String) msj.obtenerCargaUtil());
                break;
            case 28:
                ret= pol.buscarClienteDni((String) msj.obtenerCargaUtil());
                break;
        }
        return ret;
    }
    
    private void enviar(Mensaje [] msjs) throws InterruptedException
    {
        Mensaje mRet= msjs[0];
        Mensaje mAvi= msjs[1];
        conexiones.adquirir();
        for (Conexion l : conexiones)
        {
            if (l.obtenerId() == mRet.obtenerDuenio())
                l.enviar(mRet);
            else
            {
                if (mAvi != null)
                    l.enviar(mAvi);
            }
        }
        conexiones.liberar();
    }
    
    private void enviar(InputStream is, int idConexion) throws InterruptedException
    {
        conexiones.adquirir();
        for (Conexion x : conexiones)
        {
            if (x.obtenerId() == idConexion)
            {
                x.enviar(is);
                conexiones.liberar();
                return;
            }
        }
    }
    
    public boolean detener(boolean forzado) throws SQLException
    {
        try {
            if (forzado)
            {
                this.interrupt();
                conexiones.adquirir();
                for (Conexion l : conexiones)
                {
                    gestorBD.cerrarTodos(l.obtenerId());
                    l.interrupt();
                }
                gestorCO.detener();
                gestorBD.desconectar();
                conexiones.liberar();
                return true;
            }
            else
            {
                conexiones.adquirir();
                if (conexiones.isEmpty())
                {
                    gestorCO.detener();
                    this.interrupt();
                    gestorBD.desconectar();
                    conexiones.liberar();
                    return true;
                }
                else
                {
                    conexiones.liberar();
                    return false;
                }
            }
        } catch (InterruptedException ex) {/*NUNCA SE DA*/ return false;}
    }
    
    private InputStream obtenerStreamConexion(int idConexion)
    {
        try {
            conexiones.adquirir();
            for (Conexion x : conexiones)
            {
                if (x.obtenerId() == idConexion)
                    return x.obtenerEntradaArchivo();
            }
            conexiones.liberar();
            return null; //NO DEBERÍA SUCEDER.
        } catch (InterruptedException ex) {/*NADA*/ return null;}
    }
    
    public String obtenerIP() {return gestorCO.obtenerIP();}
    
    public void procesar(Mensaje msj) {colaMensajes.encolar(msj);}
    
    public void agregarConexion(Conexion lo) throws InterruptedException
    {
        if (idConexiones == Integer.MIN_VALUE)
            this.notificarError("Identificador de conexiones agotado.");
        else
        {
            lo.asignarId(idConexiones);
            lo.asignarProcesador(this);
            idConexiones-= 1;
            conexiones.adquirir();
            conexiones.add(lo);
            this.notificarConexion(conexiones.size());
            conexiones.liberar();
            lo.start();
        }
    }
    
    public void quitarConexion(Conexion lo) throws InterruptedException
    {
        gestorBD.cerrarTodos(lo.obtenerId());
        conexiones.adquirir();
        conexiones.remove(lo);
        this.notificarConexion(conexiones.size());
        conexiones.liberar();
    }
    
    public void errorDeRed(String mensaje) {this.notificarError(mensaje);}
    
    public void agregarProcesadorListener(ProcesadorListener pel) {listener= pel;}
    
    public void quitarProcesadorListener() {listener= null;}
    
    private void notificarError(String msj)
    {
        if (listener != null)
            listener.procesadorObjetoEventoError(msj);
    }
    
    private void notificarConexion(int valor)
    {
        if (listener != null)
            listener.procesadorObjetoEventoConexiones(valor);
    }
}
